/*
 * SyncTarget.java
 *
 * Created on April 11, 2007, 8:42 AM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.atomojo.app.db;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URI;
import java.util.logging.Level;
import org.atomojo.app.InfosetRepresentation;
import org.atomojo.app.admin.AdminXML;
import org.atomojo.app.auth.AuthCredentials;
import org.infoset.xml.Element;
import org.infoset.xml.InfosetFactory;
import org.infoset.xml.XMLException;
import org.infoset.xml.util.XMLWriter;
import org.restlet.Client;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.data.ChallengeResponse;
import org.restlet.data.ChallengeScheme;
import org.restlet.data.MediaType;
import org.restlet.data.Method;
import org.restlet.data.Protocol;
import org.restlet.data.Status;
import org.restlet.representation.FileRepresentation;
import org.restlet.representation.Representation;

/**
 *
 * @author alex
 */
public class RemoteApp extends XMLObject implements StoredXML
{
   DB db;
   String name;
   URI introspect;
   URI root;
   AuthCredentials auth;
   
   /** Creates a new instance of SyncTarget */
   public RemoteApp(DB db,Representation rep)
   {
      super(rep);
      this.db = db;
   }
   
   public RemoteApp(DB db,Element xml)
   {
      super(xml);
      this.name = xml.getAttributeValue("name");
      this.db = db;
   }
   
   public RemoteApp(DB db,String name)
   {
      super((Element)null);
      this.name = name;
      this.db = db;
   }
   
   public RemoteApp(DB db,String name,URI introspect,URI root,AuthCredentials auth)
   {
      super((Element)null);
      this.name = name;
      this.introspect = introspect;
      this.root = root;
      this.auth = auth;
      this.db = db;
   }
   
   public Representation getRepresentation() {
      if (rep==null && name!=null) {
         rep = new FileRepresentation(getFile(),MediaType.APPLICATION_XML);
      }
      return super.getRepresentation();
   }
   
   protected File getFile() {
      return new File(db.getRemoteAppDir(),name+".xml");
   }
   
   public void unmarshall()
      throws XMLException
   {
      if (xml==null) {
         try {
            parse();
         } catch (IOException ex) {
            throw new XMLException("Cannot load XML due to I/O exception.",ex);
         }
      }
      try {
         Element top = getElement();
         name = top.getAttributeValue("name");
         String intro = top.getAttributeValue("introspect");
         introspect = intro==null ? null : URI.create(intro);
         root = URI.create(top.getAttributeValue("root"));
         Element authE = top.getFirstElementNamed(AdminXML.NM_AUTH);
         auth = authE==null ? null : new AuthCredentials(authE.getAttributeValue("method"),authE.getAttributeValue("name"),authE.getAttributeValue("password"));
      } catch (Exception ex) {
         throw new XMLException("Cannot unmarshall XML: "+ex.getMessage(),ex);
      }
   }
   
   public void marshall()
      throws XMLException
   {
      xml = InfosetFactory.getDefaultInfoset().createItemConstructor().createDocument().createDocumentElement(AdminXML.NM_APP);
      xml.setAttributeValue("name",name);
      xml.setAttributeValue("root",root.toString());
      if (introspect!=null) {
         xml.setAttributeValue("introspect",introspect.toString());
      }
      if (auth!=null) {
         Element authE = xml.addElement(AdminXML.NM_AUTH);
         authE.setAttributeValue("method",auth.getScheme());
         authE.setAttributeValue("name",auth.getName());
         authE.setAttributeValue("password",auth.getPassword());
      }
      rep = new InfosetRepresentation(MediaType.APPLICATION_XML,xml.getDocument());
   }
   
   public String getName() 
   {
      return name;
   }
   
   public URI getIntrospection() 
   {
      return introspect;
   }
   
   public URI getRoot() 
   {
      return root;
   }
   
   public AuthCredentials getAuthCredentials() 
   {
      return auth;
   }
   
   public void setIntrospection(URI uri)
   {
      introspect = uri;
   }
   
   public void setRoot(URI uri)
   {
      root = uri;
   }
   
   public boolean exists()
   {
      return getFile().exists();
   }
   
   public boolean create()
   {
      return update();
   }
   
   public boolean delete()
   {
      return getFile().delete();
   }
   
   public boolean update()
   {
      try {
         Writer out = new OutputStreamWriter(new FileOutputStream(getFile()),"UTF-8");
         XMLWriter.writeDocument(xml.getDocument(), out);
         out.flush();
         out.close();
         return true;
      } catch (Exception ex) {
         db.getLogger().log(Level.SEVERE,"Cannot store remote app to "+getFile()+" due to exception.",ex);
         return false;
      }
   }
   
   public boolean isAvailable()
   {
      URI uri = getIntrospection();
      if (uri==null) {
         uri = getRoot();
      }
      AuthCredentials auth = getAuthCredentials();
      Client client = new Client(new Context(db.getLogger()),Protocol.valueOf(uri.getScheme()));
      client.getContext().getAttributes().put("hostnameVerifier", org.apache.commons.ssl.HostnameVerifier.DEFAULT);
      
      Request request = new Request(Method.HEAD,uri.toString());
      request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_BASIC,auth.getName(),auth.getPassword()));
      Response response = client.handle(request);
      return response.getStatus().isSuccess() || response.getStatus().equals(Status.CLIENT_ERROR_NOT_FOUND);
   }
   
}
